#include "stdafx.h"
#include "Extensions.h"
#include "Exceptions.h"
#include <vector>
#include <string>

PFNGLTEXIMAGE3DPROC glTexImage3D = NULL;
PFNGLACTIVETEXTUREPROC glActiveTexture = NULL;
PFNGLGENBUFFERSPROC glGenBuffers = NULL;                     
PFNGLBINDBUFFERPROC glBindBuffer = NULL;                     
PFNGLBUFFERDATAPROC glBufferData = NULL;                     
PFNGLDELETEBUFFERSPROC glDeleteBuffers = NULL;               
PFNGLMAPBUFFERPROC glMapBuffer = NULL;                       
PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;
PFNGLGENQUERIESPROC glGenQueries;
PFNGLDELETEQUERIESPROC glDeleteQueries;
PFNGLISQUERYPROC glIsQuery;
PFNGLBEGINQUERYPROC glBeginQuery;
PFNGLENDQUERYPROC glEndQuery;
PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv;
PFNGLDRAWBUFFERSPROC glDrawBuffers = NULL;
PFNGLATTACHSHADERPROC glAttachShader = NULL;
PFNGLCOMPILESHADERPROC glCompileShader = NULL;
PFNGLCREATEPROGRAMPROC glCreateProgram = NULL;
PFNGLCREATESHADERPROC glCreateShader = NULL;
PFNGLDELETEPROGRAMPROC glDeleteProgram = NULL;
PFNGLDELETESHADERPROC glDeleteShader = NULL;
PFNGLDETACHSHADERPROC glDetachShader = NULL;
PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = NULL;
PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = NULL;
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = NULL;
PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL;
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL;
PFNGLGETSHADERIVPROC glGetShaderiv = NULL;
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL;
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL;
PFNGLLINKPROGRAMPROC glLinkProgram = NULL;
PFNGLSHADERSOURCEPROC glShaderSource = NULL;
PFNGLUSEPROGRAMPROC glUseProgram = NULL;
PFNGLUNIFORM1FPROC glUniform1f = NULL;
PFNGLUNIFORM1IPROC glUniform1i = NULL;
PFNGLUNIFORM2FVPROC glUniform2fv = NULL;
PFNGLUNIFORM3FVPROC glUniform3fv = NULL;
PFNGLVALIDATEPROGRAMPROC glValidateProgram = NULL;

// GL_EXT_framebuffer_object
PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = NULL;
PFNGLDELETERENDERBUFFERSEXTPROC glDeleteRenderbuffersEXT = NULL;
PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = NULL;
PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = NULL;
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = NULL;
PFNGLDELETEFRAMEBUFFERSEXTPROC glDeleteFramebuffersEXT = NULL;
PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = NULL;
PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glFramebufferTexture1DEXT = NULL;
PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glFramebufferTexture2DEXT = NULL;
PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glFramebufferTexture3DEXT = NULL;
PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC glFramebufferTextureLayerEXT = NULL;
PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = NULL;
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glGetFramebufferAttachmentParameterivEXT = NULL;
PFNGLGENERATEMIPMAPEXTPROC glGenerateMipmapEXT = NULL;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = NULL;

// GL_ARB_color_buffer_float
PFNGLCLAMPCOLORARBPROC glClampColorARB = NULL;

// GL_EXT_texture_integer
PFNGLCLEARCOLORIIEXTPROC glClearColorIiEXT = NULL;

// GL_EXT_gpu_shader4
PFNGLBINDFRAGDATALOCATIONEXTPROC glBindFragDataLocationEXT = NULL;

#define INIT_ENTRY_POINT(type, funcname)											\
	funcname = (type)wglGetProcAddress(#funcname);									\
	if ( !funcname )																\
{																					\
	throw GraphicsCapabilityException(#funcname "() function not available.");		\
}

Extensions::Extensions(void)
{
	SetupOpenGLExtensions();
}

void Extensions::SetupOpenGLExtensions()
{
	float version = 0.0f;
	std::stringstream versionStream((const char*)glGetString(GL_VERSION));
	versionStream >> version;

	if(version < 2.1f)
		throw GraphicsCapabilityException("This application requires OpenGL 2.1 or greater.");

	std::vector<std::string> extensions;
	extensions.push_back(std::string("GL_ARB_color_buffer_float"));
	extensions.push_back(std::string("GL_EXT_framebuffer_object"));
	extensions.push_back(std::string("GL_ARB_texture_rectangle"));
	extensions.push_back(std::string("GL_ARB_texture_float"));
	extensions.push_back(std::string("GL_EXT_texture_integer"));
	extensions.push_back(std::string("GL_EXT_texture_array"));
	extensions.push_back(std::string("GL_EXT_gpu_shader4"));

	std::string extensionsString = std::string((const char*)glGetString(GL_EXTENSIONS));
	std::stringstream extensionsStream("This application requires the following extensions to OpenGL 2.1: ");
	bool extensionNotFound = false;
	for (unsigned int i = 0; i < extensions.size(); i++)
	{
		if (extensionsString.find(extensions[i]) == std::string::npos)
		{
			extensionNotFound = true;
			extensionsStream << std::endl << extensions[i];
		}
	}

	if(extensionNotFound)
		throw GraphicsCapabilityException(extensionsStream.str());

	INIT_ENTRY_POINT(PFNGLACTIVETEXTUREPROC, glActiveTexture);
	INIT_ENTRY_POINT(PFNGLTEXIMAGE3DPROC, glTexImage3D);
	INIT_ENTRY_POINT(PFNGLDRAWBUFFERSPROC, glDrawBuffers);
	INIT_ENTRY_POINT(PFNGLGENBUFFERSPROC, glGenBuffers);          
	INIT_ENTRY_POINT(PFNGLBINDBUFFERPROC, glBindBuffer);                     
	INIT_ENTRY_POINT(PFNGLBUFFERDATAPROC, glBufferData);                     
	INIT_ENTRY_POINT(PFNGLDELETEBUFFERSPROC, glDeleteBuffers);               
	INIT_ENTRY_POINT(PFNGLMAPBUFFERPROC, glMapBuffer);                       
	INIT_ENTRY_POINT(PFNGLUNMAPBUFFERPROC, glUnmapBuffer);      
	INIT_ENTRY_POINT(PFNGLGENQUERIESPROC, glGenQueries);
	INIT_ENTRY_POINT(PFNGLDELETEQUERIESPROC, glDeleteQueries);
	INIT_ENTRY_POINT(PFNGLISQUERYPROC, glIsQuery);
	INIT_ENTRY_POINT(PFNGLBEGINQUERYPROC, glBeginQuery);
	INIT_ENTRY_POINT(PFNGLENDQUERYPROC, glEndQuery);
	INIT_ENTRY_POINT(PFNGLGETQUERYOBJECTUIVPROC, glGetQueryObjectuiv);
	INIT_ENTRY_POINT(PFNGLATTACHSHADERPROC, glAttachShader);
	INIT_ENTRY_POINT(PFNGLCOMPILESHADERPROC, glCompileShader);
	INIT_ENTRY_POINT(PFNGLCREATEPROGRAMPROC, glCreateProgram);
	INIT_ENTRY_POINT(PFNGLCREATESHADERPROC, glCreateShader);
	INIT_ENTRY_POINT(PFNGLDELETEPROGRAMPROC, glDeleteProgram);
	INIT_ENTRY_POINT(PFNGLDELETESHADERPROC, glDeleteShader);
	INIT_ENTRY_POINT(PFNGLDETACHSHADERPROC, glDetachShader);
	INIT_ENTRY_POINT(PFNGLGETACTIVEATTRIBPROC, glGetActiveAttrib);
	INIT_ENTRY_POINT(PFNGLGETACTIVEUNIFORMPROC, glGetActiveUniform);
	INIT_ENTRY_POINT(PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation);
	INIT_ENTRY_POINT(PFNGLGETPROGRAMIVPROC, glGetProgramiv);
	INIT_ENTRY_POINT(PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog);
	INIT_ENTRY_POINT(PFNGLGETSHADERIVPROC, glGetShaderiv);
	INIT_ENTRY_POINT(PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog);
	INIT_ENTRY_POINT(PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation);
	INIT_ENTRY_POINT(PFNGLLINKPROGRAMPROC, glLinkProgram);
	INIT_ENTRY_POINT(PFNGLSHADERSOURCEPROC, glShaderSource);
	INIT_ENTRY_POINT(PFNGLUSEPROGRAMPROC, glUseProgram);
	INIT_ENTRY_POINT(PFNGLUNIFORM1FPROC, glUniform1f);
	INIT_ENTRY_POINT(PFNGLUNIFORM1IPROC, glUniform1i);
	INIT_ENTRY_POINT(PFNGLUNIFORM2FVPROC, glUniform2fv);
	INIT_ENTRY_POINT(PFNGLUNIFORM3FVPROC, glUniform3fv);
	INIT_ENTRY_POINT(PFNGLVALIDATEPROGRAMPROC, glValidateProgram);

	// GL_EXT_framebuffer_object
	INIT_ENTRY_POINT(PFNGLBINDRENDERBUFFEREXTPROC, glBindRenderbufferEXT);
	INIT_ENTRY_POINT(PFNGLDELETERENDERBUFFERSEXTPROC, glDeleteRenderbuffersEXT);
	INIT_ENTRY_POINT(PFNGLGENRENDERBUFFERSEXTPROC, glGenRenderbuffersEXT);
	INIT_ENTRY_POINT(PFNGLRENDERBUFFERSTORAGEEXTPROC, glRenderbufferStorageEXT);
	INIT_ENTRY_POINT(PFNGLBINDFRAMEBUFFEREXTPROC, glBindFramebufferEXT);
	INIT_ENTRY_POINT(PFNGLDELETEFRAMEBUFFERSEXTPROC, glDeleteFramebuffersEXT);
	INIT_ENTRY_POINT(PFNGLGENFRAMEBUFFERSEXTPROC, glGenFramebuffersEXT);
	INIT_ENTRY_POINT(PFNGLFRAMEBUFFERTEXTURE1DEXTPROC, glFramebufferTexture1DEXT);
	INIT_ENTRY_POINT(PFNGLFRAMEBUFFERTEXTURE2DEXTPROC, glFramebufferTexture2DEXT);
	INIT_ENTRY_POINT(PFNGLFRAMEBUFFERTEXTURE3DEXTPROC, glFramebufferTexture3DEXT);
	INIT_ENTRY_POINT(PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC, glFramebufferTextureLayerEXT);
	INIT_ENTRY_POINT(PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC, glFramebufferRenderbufferEXT);
	INIT_ENTRY_POINT(PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC, glGetFramebufferAttachmentParameterivEXT);
	INIT_ENTRY_POINT(PFNGLGENERATEMIPMAPEXTPROC, glGenerateMipmapEXT);
	INIT_ENTRY_POINT(PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC, glCheckFramebufferStatusEXT);

	// GL_ARB_color_buffer_float
	INIT_ENTRY_POINT(PFNGLCLAMPCOLORARBPROC, glClampColorARB);

	// GL_EXT_texture_integer
	INIT_ENTRY_POINT(PFNGLCLEARCOLORIIEXTPROC, glClearColorIiEXT);

	// GL_EXT_gpu_shader4
	INIT_ENTRY_POINT(PFNGLBINDFRAGDATALOCATIONEXTPROC, glBindFragDataLocationEXT);


}
